package org.si.icon;

import java.awt.*;
import java.awt.geom.AffineTransform;
import javax.swing.Icon;
import javax.swing.SwingConstants;
import org.si.utils.LocationPolicy;
import org.si.utils.ScalePolicy;

/**
 * Wraps a standard icon adding ScalableIcon properties to it. Use this class if you intend to place a normal Icon as a
 * child of a ScalableIcon. This class uses a ScalePolicy and LocationPolicy to allow you to configure how the simpler
 * Icon behaves under scaling, the default is to not scale that icon and to center it in the target area.
 *
 * @author Matt Nathan
 */
public class IconWrapper extends AbstractScalableIcon {

    private final Icon simpleIcon;
    private final ScalePolicy scalePolicy;
    private final LocationPolicy locationPolicy;





    /**
     * Wrap the simple icon to make it scalable. The default is to scale around it keeping the simpleIcon at it's
     * preferred size and placing it in the middle of the target paint area.
     *
     * @param simpleIcon The icon to wrap.
     */
    public IconWrapper(Icon simpleIcon) {
        this(simpleIcon, null, null);
    }





    /**
     * Wrap a simpleIcon to make it scalable. The way the simpleIcon is scaled is defined by the given ScalePolicy
     * instance. If the simpleIcon doens't fit the paint area then it will be centered.
     *
     * @param simpleIcon  The icon to wrap
     * @param scalePolicy The scale policy to use.
     */
    public IconWrapper(Icon simpleIcon, ScalePolicy scalePolicy) {
        this(simpleIcon, scalePolicy, null);
    }





    /**
     * Wrap a simple icon to make it scalable. This will not scale the simpleIcon and will position it according to the
     * given LocationPolicy within the target paint area.
     *
     * @param simpleIcon     The icon to wrap
     * @param locationPolicy The location policy to use.
     */
    public IconWrapper(Icon simpleIcon, LocationPolicy locationPolicy) {
        this(simpleIcon, null, locationPolicy);
    }





    /**
     * Wrap a simpleIcon to make it scalable. This uses the given ScalePolicy and LocationPolicy to define what to do
     * with the simpleIcon.
     *
     * @param simpleIcon     The icon to wrap
     * @param scalePolicy    The scale policy to use.
     * @param locationPolicy The location policy to use.
     */
    public IconWrapper(Icon simpleIcon, ScalePolicy scalePolicy, LocationPolicy locationPolicy) {
        if (scalePolicy == null) {
            scalePolicy = ScalePolicy.NONE;
        }
        if (locationPolicy == null) {
            locationPolicy = LocationPolicy.valueOf(SwingConstants.CENTER);
        }
        this.simpleIcon = simpleIcon;
        this.scalePolicy = scalePolicy;
        this.locationPolicy = locationPolicy;
    }





    @Override
    public void fitInto(Component c, int width, int height, Dimension result) {
        scalePolicy.fitInto(
                new Dimension(getIconWidth(), getIconHeight()),
                new Dimension(width, height),
                result);
    }





    @Override
    public void paintIcon(Component c, Graphics g, int x, int y, int width, int height) {
        Dimension targetSize = new Dimension(width, height);
        Dimension sourceSize = new Dimension(getIconWidth(), getIconHeight());

        Dimension resultSize = scalePolicy.fitInto(sourceSize, targetSize, null);

        Point paintLocation = locationPolicy.locate(resultSize, targetSize, null);
        paintLocation.x += x;
        paintLocation.y += y;

        if (resultSize.equals(sourceSize)) {
            // no scaling required
            simpleIcon.paintIcon(c, g, paintLocation.x, paintLocation.y);
        } else {
            // need to scale the graphics so that the icon will draw at the correct size
            // todo: maybe allow different types of scale method: GRAPHICS, IMAGE, etc
            Graphics2D g2 = (Graphics2D) g.create();

            // to scale the graphics correctly we need to translate the top left corner of the draw region to (0,0)
            // scale
            // then translate back to paintLocation
            AffineTransform trans = g2.getTransform();
            trans.translate(paintLocation.x, paintLocation.y);
            trans.scale(resultSize.getWidth() / sourceSize.getWidth(),
                        resultSize.getHeight() / sourceSize.getHeight());

            g2.setTransform(trans);

            simpleIcon.paintIcon(c, g2, 0, 0);

            g2.dispose();
        }
    }





    /**
     * Returns the icon width of the simpleIcon.
     *
     * @return simpleIcon.getIconWidth();
     */
    @Override
    public int getIconWidth() {
        return simpleIcon.getIconWidth();
    }





    /**
     * Returns the icon height of the simpleIcon.
     *
     * @return simpleIcon.getIconWidth();
     */
    @Override
    public int getIconHeight() {
        return simpleIcon.getIconHeight();
    }





    public Icon getSimpleIcon() {
        return simpleIcon;
    }





    public ScalePolicy getScalePolicy() {
        return scalePolicy;
    }





    public LocationPolicy getLocationPolicy() {
        return locationPolicy;
    }
}
